package com.atwisdom.star.core.bsl.functionClass.statistics;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.atwisdom.star.common.function.AtwisdomMapFunction;
import com.atwisdom.star.common.util.JsonUtil;
import com.atwisdom.star.common.util.SpringContextUtil;
import com.atwisdom.star.common.vo.ActionData;
import com.atwisdom.star.core.bsl.event.ServiceEnum;
import com.atwisdom.star.common.pojo.*;
import com.atwisdom.star.exception.pojo.BusinessException;
import com.googlecode.aviator.AviatorEvaluator;
import com.jayway.jsonpath.JsonPath;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 *
 * @Date: 2023-11-30 13:43
 * todo:
 * 1. 统计中间表， 时间差的计算方式需要计算
 * 2. 设备一天的总运行时间，需要根据日期，定时填充到相应数据中
 */
@Slf4j
@Service
public class StatisticsService {

    static {
        try {
            AviatorEvaluator.addStaticFunctions("mapFun", AtwisdomMapFunction.class);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param actionData
     * @return
     * 1. 根据StatisticsItem获取实例，并保存结果，一次查询可以提取出多个结果，保存到指定的参数中，用于后期保存
     *
     * 计算时的参数封装到map中，如果想使用上一步的结果，即全局参数，还需要一个全局的map，公式配置的时候，都从全局中提取变量
     * 从查询结果中拿到数据， 根据jsonPath取出数据，将数据存放到calculateParamMa
     * calculateParamMap本身又存放到globalMap中，
     * 公式计算时，以globalMap为所有参数，
     * 计算的结果保存在哪里？？？
     * 第1次计算， expression可能为空，视为只是提取数据
     * 第2次计算， expression可能用到了第一次的数据
     * 第3次计算， expression可能用到了第一次的数据，第二次的结果
     */
    public Map<String, Object> calculate(ActionData<JSONObject> actionData) {
        JSONObject statisticsParamJson = actionData.getStatisticsParam();
        StatisticsParamData statisticsParamData = actionData.getStatisticsParamData();
        JSONObject statisticsParamDataJson = JsonUtil.replaceParam(statisticsParamJson, JsonUtil.toJSONObject(statisticsParamData));
        statisticsParamData = JsonUtil.toJavaBean(statisticsParamDataJson, StatisticsParamData.class);

        List<String> returnFieldList = statisticsParamData.getReturnFieldList();
        Map<String, Object> globalMap = statisticsParamData.getGlobalMap();
        List<StatisticsItem> statisticsItemList = statisticsParamData.getStatisticsItemList();
        try {
            for (int i = 0; i < statisticsItemList.size(); i++) {
                StatisticsItem statisticsItem = statisticsItemList.get(i);
                String statisticsItemKey = statisticsItem.getKey();
                String serviceModel = statisticsItem.getServiceModel();
                String className = statisticsItem.getClassName();
                String condition = statisticsItem.getCondition();
                String model = statisticsItem.getModel();
                String parentId = statisticsItem.getParentId();
                Object resultInfo = null;
                if (StrUtil.isNotBlank(className)) {
                    ServiceEnum serviceEnum = ServiceEnum.valueOf(serviceModel);
                    Object service = SpringContextUtil.getBean(serviceEnum.getServiceId());
                    ActionData<JSONObject> queryActionData = new ActionData<>();
                    queryActionData.setClassName(className);
                    queryActionData.setCondition(condition);
                    queryActionData.setModel(model);
                    queryActionData.setParentId(parentId);
                    // 调用的方法写死，因为统计只有查询，需要别的方法时，可以单独增加action
                    Method method = service.getClass().getMethod("queryJson", ActionData.class);
                    resultInfo = method.invoke(service, queryActionData);
                }

                // 开始处理接口返回的结果
                List<StatisticsItemResult> resultExpressionList = statisticsItem.getResultList();
                Map<String, Object> calculateParamMap = new HashMap<>();
                globalMap.put(statisticsItemKey, calculateParamMap);
                for (StatisticsItemResult statisticsItemResult : resultExpressionList) {
                    String jsonPath = statisticsItemResult.getJsonPath();
                    String key = statisticsItemResult.getKey();
                    if (StrUtil.isNotBlank(key) && StrUtil.isNotBlank(jsonPath) && resultInfo != null) {
                        Object jsonPathResult = JsonPath.read(resultInfo, jsonPath);
                        calculateParamMap.put(key, jsonPathResult);
                    }
                    String expression = statisticsItemResult.getExpression();
                    String resultKey = statisticsItemResult.getResultKey();
                    if (StrUtil.isNotBlank(expression) && StrUtil.isNotBlank(resultKey)) {
                        Object expressionResult = AviatorEvaluator.execute(expression, globalMap);
                        calculateParamMap.put(resultKey, expressionResult);
                    }
                }
            }
        } catch (NoSuchMethodException e) {
            log.error("没有该方法可以调用, {}", e);
            throw new BusinessException(e.getCause().getMessage());
        }catch (InvocationTargetException e) {
            log.error("调用方法出错, {}", e);
            throw new BusinessException(e.getCause().getMessage());
        } catch (IllegalAccessException e) {
            log.error("没有访问该方法权限");
            throw new BusinessException(e);
        }
        if (CollectionUtil.isNotEmpty(returnFieldList)) {
            Map<String, Object> returnMap = new HashMap<>();
            returnFieldList.forEach(returnField -> {
                String[] returnArray = returnField.split(":");
                if (returnArray.length == 1) {
                    // 说明只声明了jsonPath, 则默认返回变量名称与jsonPath同名
                    returnArray[1] = returnArray[0];
                }
                returnMap.put(returnArray[1],  JsonPath.read(globalMap, returnArray[0]));
            });
            return returnMap;
        }
        return globalMap;
    }
}
